/*
 * FS.c
 *
 *  Created on: 21/10/2011
 *      Author: utn_so
 */

#include <ctype.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/un.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>

#include "config.h"
#include "nipc.h"

#include "plani.h"
#include "raid.h"
#include "consola.h"

extern pthread_mutex_t descriptor;
extern struct descriptor_t *tablaDesc, *ultimo, *tablaprox;
extern int cantSecTotal;
extern int fdConsola;
int fdFs = -1;
int numeroDesc = 0;

extern struct t_sockets *tablaSock;

int cantidadSock = 0;


int crearSockConsola() {
	int sock, retval, errno, fd, *fdParam;
	char pathSockUnix[14];
	struct sockaddr_un dir;
	nipcMessage msg = nipc_initmsg;
	pthread_t tid;
	int h;

	dir.sun_family = AF_UNIX;

	getConfig("pathsockunix", &pathSockUnix);

	memset(dir.sun_path, '\0', 108);
	strcpy(dir.sun_path, pathSockUnix);

	sock = socket(AF_UNIX, SOCK_STREAM, 0);

	if (sock == -1) {
		printf("Error al crear el socket. Error n°: %d, descripcion: %s.\n",
				errno, strerror(errno));
		return 1;
	}

	if (bind(sock, (struct sockaddr*) &dir, sizeof(dir)) == -1) {
		retval = errno;
		if (retval == EADDRINUSE)
		{
			system("rm /tmp/raid.temp");
			printf("Error system: %d,%s\n.", errno, strerror(errno));
			if (0 > bind(sock, (struct sockaddr*) &dir, sizeof(dir))) {
				printf(
						"Imposible bindear el socket para la consola. Error n°: %d, descripcion: %s.\n",
						errno, strerror(errno));
				close(sock);
			}
		} else {
			printf(
					"Imposible bindear el socket para la consola. Error n°: %d, descripcion: %s.\n",
					errno, strerror(errno));
			close(sock);
		}
	}

	listen(sock, 1);

	fd = accept(sock, NULL, NULL);

	retval = nipc_recv(&msg, fd);
	h = atoi(strtok(msg.payload, ";"));

	if ((retval == NIPC_ERROR) || (msg.payloadLength == 0)
			|| (h != CONSOLA_HANDSHAKE)) {

		printf("error de handshake\n");
		nipc_0payload(&msg, fdConsola);
		sprintf(msg.payload, "%d", HANDSHAKE_FAIL);
		msg.payloadLength = strlen(msg.payload) + 1;
		msg.type = '0';

		nipc_send(&msg, fdConsola);
		close(fdConsola);

	} else {

		sprintf(msg.payload, "%d;", HANDSHAKE_OK);
		msg.payloadLength = strlen(msg.payload) + 1;

		tablaDesc = agregarTablaDesc(fdConsola, 0);

		// Mandamos la respuesta del handshake
		nipc_send(&msg, fdConsola);

		fdParam = (int *) malloc(sizeof(int));

		*fdParam = fdConsola;

//		pthread_create(&tid, NULL, atenderConsola, (void*)fdParam);

		free(fdParam);

	}

	return fd;
}

void agregarFdEnTablaSock(int fdCon, int tipoConexion) {
	struct t_sockets *regInsertar;

	regInsertar = (struct t_sockets*) malloc(sizeof(struct t_sockets));

	regInsertar->fd = fdCon;
	regInsertar->proceso = tipoConexion;
	regInsertar->siguiente = tablaSock;

	tablaSock = regInsertar;
}

int estaConectado(int fdCon, int idDisco) {
	nipcMessage nipc;
	int h;

	nipc.payload = (char*) malloc(sizeof(idDisco) + 1 + 1);

	h = descriptorDelPlani(idDisco);
	nipc_0payload(&nipc, h);
	sprintf(nipc.payload, "%d;", idDisco);

	nipc.type = conectado_c;
	nipc_send(&nipc, h);

	if ((h == NIPC_ERROR) || (h == NIPC_CONNECTION_LOST)) {
		h = 0;
		free(nipc.payload);
		return 0;
	} else {
		h = 1;
		free(nipc.payload);
		return 1;
	}
	return 0;
}

int cantidadPlani() {
	struct descriptor_t *aux = tablaDesc;
	int cantPlani = 0;

	while (aux != NULL) {
		if (estaConectado(aux->descriptorSoc, aux->idDisco)) {
			++cantPlani;
		} else {
			eliminarPlani(aux->idDisco);
		}
		aux = aux->siguiente;
	}

	return cantPlani;
}

int agregarTablaDesc(int i, int idDisco) {
	struct descriptor_t *actual;

	actual = (struct descriptor_t *) malloc(sizeof(struct descriptor_t));

	// copiamos el descriptor de la conexion
	actual->descriptorSoc = i;
	actual->idDisco = idDisco;
	actual->pedidos = NULL;
	actual->bitmap = NULL;
	actual->estado = 1;
	actual->siguiente = NULL;

	pthread_mutex_lock(&descriptor);

	if (tablaDesc == NULL)
	{
		tablaDesc = actual;
	} else {

		ultimo->siguiente = actual;
	}

	if (sem_init(&(actual->contador), 0, 0) < 0) {
		perror("sem_init");
	}

	crearBitMap(cantSecTotal, actual->bitmap);

	ultimo = actual;
	pthread_mutex_unlock(&descriptor);

	return 1;
}

int atenderFS(int fdCon) {
	nipcMessage nipc;
	int aux = 0, sector;
	char *buffer;
	char pedido;
	struct descriptor_t *l;

	l = tablaDesc;

	aux = nipc_recv(&nipc, fdCon);

	if (aux == NIPC_ERROR_NONE) {

		/*		nipc.payloadLength = 0;
		 nipc.type = '0';
		 memset(nipc.payloadID, 0, 6);
		 sprintf(nipc.payloadID,"%d", fdCon);

		 nipc.payload = NULL;*/

		switch (nipc.type) {
		case read_c: // 1 correspone a lectura

			pedido = read_c;
			sector = atoi(strtok(nipc.payload, ";"));
			buffer = NULL;
			break;
		case write_c: // 2 corresponde a escritura

			pedido = write_c; // LE TENGO QUE DECIR A LOS CHICOS QUE PARA MANDARME ESCRITURA
			//EN EL PAYLOAD ME TIENE QUE MANDAR: SECTOR";"LOQUEQUIERENESCRIBIR!
			sector = atoi(strtok(nipc.payload, ";"));
			buffer = nipc.payload;
			break;

		default:
			/* mal mensaje*/
			break;
		}

		tablaprox = agregarPedido(fdCon, pedido, sector, buffer);

	}
	return 0;
}

/* ME PARECE QUE ESTO NI LO VOY A VERIFICAR
 int existeFS(int fdCon)
 {
 struct descriptor_t * h;

 h = tablaDesc;
 while((h != NULL) && (h->descriptorSoc != fdCon))
 {
 h = h->siguiente;
 }

 if(h == NULL)
 {
 return(0);
 }else{
 return(1);
 }
 }
 */
int descriptorDelPlani( idDisco) {
	struct descriptor_t *h = tablaDesc;

	while ((h != NULL) && (h->idDisco != idDisco)) {
		h = h->siguiente;
	}
	if (h == NULL)
	{
		return 0;
	} else {
		return h->descriptorSoc;
	}
	return 0;

}

int idPlaniDelDescriptor( pDescriptor) {
	struct descriptor_t *h = tablaDesc;

	while ((h != NULL) && (h->descriptorSoc != pDescriptor)) {
		h = h->siguiente;
	}
	if (h == NULL)
	{
		return 0;
	} else {
		return h->idDisco;
	}
	return 0;

}

int eliminarPlani(int idDisco) {
	struct descriptor_t *h = NULL;
	struct descriptor_t *aux = NULL;

	h = tablaDesc;
	if (h == NULL)
	{
		exit(0);
	}
	while ((h->siguiente != NULL) && (h->siguiente->idDisco != idDisco)) {
		h = h->siguiente;
	}
	if (h == NULL)
	{
		return 0;
	} else {
		aux = h->siguiente;
		sem_destroy(&aux->contador);
		h->siguiente = h->siguiente->siguiente;
		free(aux);
		return 1;
	}
	return 0;

}

int hayUnplani() {
	int h = 0;
	struct descriptor_t *aux;

	aux = tablaDesc;
	while ((aux != NULL) && (h != 1)) {
		if (estaConectado(aux->descriptorSoc, aux->idDisco)) {
			return 1;
		} else {
			return 0;
		}
	}

	return 0;
}

int crearSockListener(int fdmax) {
	int fdSocket;
	char localip[13];
	unsigned int fsport;

	getConfig("puerto", &fsport);
	getConfig("localip", &localip);

	fdSocket = sockets_open_server(localip, fsport, fdmax);

	if (-1 == fdSocket) {
		exit(1);
	}

	return fdSocket;
}
/*
void pollIn(int fd) {
	struct epoll_event event;
	int s, errno;

	memset(&event, 0, sizeof(event));

	event.data.fd = fd;
	event.events = EPOLLIN;
	s = epoll_ctl(fdPoll, EPOLL_CTL_ADD, fd, &event);

	if (s == -1) {
		printf("Error al agregar un fd. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
		exit(1);
	}
}
*/

/*void atenderPoll() {
	struct epoll_event *events;
	struct epoll_event event;
	int i, j = 0, posicion, errno;
	size_t maxEvents = 20; // cualca, tiene que salir de fdmax y la cantidad de planis y consola
	int cantWait = -1;
	struct t_sockets *nodoSocket;

	events = calloc(maxEvents, sizeof(event));

	do {

		cantWait = epoll_wait(fdPoll, events, maxEvents, -1);

		if (cantWait == -1) {
			printf("Error en el wait. Error: %d, %s\n", errno, strerror(errno));

		}

		for (i = 0; i < cantWait; i++) {
			nodoSocket = tablaSock;

			while ((nodoSocket != NULL) && (nodoSocket->fd != events[i].data.fd)) {
				nodoSocket = nodoSocket->siguiente;
			}

			switch (nodoSocket->proceso) {
			case P_CONSOLA:
//					atenderConsola(nodoSocket->fd);
				break;
			case P_LISTENER:
				atenderListener(fdPoll, nodoSocket->fd);
				break;
			case P_FS:
				atenderFS(nodoSocket->fd);
				break;
			case P_PPD:
				atenderPlani(nodoSocket->fd);
				break;
			}
		}
	} while (cantWait > 0);
	free(events);
}
*/
void atenderListener(int fdPoll, int fdSocket) {
	int retval, fdCon, idDisco, m = 0, *fdParam = NULL;
//	int					fdConsola; //todo si hay consola
	unsigned int size;
	struct sockaddr_in dir;
	nipcMessage msg;
	char n[10] = "";
	pthread_t idPedido;

	size = sizeof(dir);
	fdCon = accept(fdSocket, (struct sockaddr*) &dir, &size);
	if (fdCon == -1) {
		perror("accept");
	}

	pollIn(fdCon);

	retval = nipc_recv(&msg, fdCon);
//	puts(msg.payload);

	if (msg.type != '0') {
		printf("error del handshake\n");
	}
	if (msg.payload == NULL)
	{
		m = hayUnplani();
		agregarFdEnTablaSock(fdCon, P_FS);
		if (!m) // || existeFS(i) si quiero verificar esto
		{
			close(fdCon);
			// mandamos la respuesta al handshake
			msg.payloadLength = 0;
			msg.type = '0';
			memset(msg.payloadID, 0, 6);
			sprintf(msg.payloadID, "%d", fdCon);

			sprintf(n, "%d;", HANDSHAKE_FAIL);
			msg.payload = (char*) malloc(strlen(n) + 1);
			bzero(msg.payload, strlen(n) + 1);

			sprintf(msg.payload, "%s;", n);
			msg.payloadLength = (strlen(msg.payload) + 1);

			nipc_send(&msg, fdCon);

			printf("no hay plani o ya existe el fs\n");

		} else if (m) {

			puts("handshake correcto con el fs");

			//añado el desc nuevo a la lista de descriptores
			idDisco = 1; // por ser fs
			tablaDesc = agregarTablaDesc(fdCon, idDisco);

			//el handshake esta bien
			msg.payloadLength = 0;
			msg.type = '0';
			memset(msg.payloadID, 0, 6);
			sprintf(msg.payloadID, "%d", fdCon);

			sprintf(n, "%d;", HANDSHAKE_OK);
			msg.payload = (char*) malloc(strlen(n) + 1);
			bzero(msg.payload, strlen(n) + 1);

			sprintf(msg.payload, "%s;", n);
			msg.payloadLength = (strlen(msg.payload) + 1);
			// mandamos la respuesta al handshake
			nipc_send(&msg, fdCon);
		}

	} else {
		agregarFdEnTablaSock(fdCon, P_PPD);

		puts("handshake con plani");

		idDisco = atoi(strtok(msg.payload, ";"));
		cantSecTotal = atoi(strtok(NULL, ";"));

		//añado el desc nuevo a la lista de descriptores
		agregarTablaDesc(fdCon, idDisco);

		//el handshake esta bien
		nipc_0payload(&msg, fdCon);

		sprintf(n, "%d;%d", idDisco, HANDSHAKE_OK);
		msg.payload = (char*) malloc(strlen(n) + 1);

		sprintf(msg.payload, "%d;%d", idDisco, HANDSHAKE_OK);

		msg.payloadLength = strlen(msg.payload) + 1;
		// mandamos la respuesta al handshake
		nipc_send(&msg, fdCon);

		//le envio a la consola para que imprima
//		msg.type = nuevo_c; //nuevo plani
//
//		sprintf(msg.payload, "%d;", idDisco);
//
//		msg.payloadLength = strlen(msg.payload) + 1;
//
//		memset(msg.payloadID, 0, 16);
//		sprintf(msg.payloadID,"%d", fdConsola);
//
//		nipc_send(&msg, fdConsola);
//
//
//		//le envio a la consola para que imprima
//		msg.type = funcionamiento_c; //entro
//
//		msg.payload = NULL;
//
//		msg.payloadLength = 0;
//
//		memset(msg.payloadID, 0, 16);
//		sprintf(msg.payloadID,"%d", fdConsola);
//
//		nipc_send(&msg, fdConsola);

		free(msg.payload);

		fdParam = (int *) malloc(sizeof(int));

		*fdParam = idDisco;
		pthread_create(&idPedido, NULL, redundancia, (void*) fdParam);


		fdParam = (int *) malloc(sizeof(int));

		*fdParam = fdCon;

		pthread_create(&idPedido, NULL, enviarPlani, (void*) fdParam);


	}
}
